package edu.cibertec.sicsolutions.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

 
public final class ClassUtil {
	
	private ClassUtil(){		
	}
	
    /**
     * <p>The package separator character: <code>'&#x2e;' == {@value}</code>.</p>
     */
    public static final char PACKAGE_SEPARATOR_CHAR = '.';

    /**
     * <p>The package separator String: <code>"&#x2e;"</code>.</p>
     */
    public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);

    /**
     * <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
     */
    public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
	
    /**
     * <p>Gets the class name minus the package name for an <code>Object</code>.</p>
     *
     * @param object  the class to get the short name for, may be null
     * @param valueIfNull  the value to return if null
     * @return the class name of the object without the package name, or the null value
     */
    public static String getShortClassName(Object object, String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getShortClassName(object.getClass().getName());
    }

    /**
     * <p>Gets the class name minus the package name from a <code>Class</code>.</p>
     *
     * @param cls  the class to get the short name for.
     * @return the class name without the package name or an empty string
     */
    public static String getShortClassName(Class cls) {
        if (cls == null) {
            return StringUtil.STRING_EMPTY;
        }
        return getShortClassName(cls.getName());
    }

    /**
     * <p>Gets the class name minus the package name from a String.</p>
     *
     * <p>The string passed in is assumed to be a class name - it is not checked.</p>
     *
     * @param className  the className to get the short name for
     * @return the class name of the class without the package name or an empty string
     */
    public static String getShortClassName(String className) {
        if (className == null) {
            return StringUtil.STRING_EMPTY;
        }
        if (className.length() == 0) {
            return StringUtil.STRING_EMPTY;
        }

        int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
        int innerIdx = className.indexOf(
                INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1);
        String out = className.substring(lastDotIdx + 1);
        if (innerIdx != -1) {
            out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
        }
        return out;
    }

    // Package name
    // ----------------------------------------------------------------------
    /**
     * <p>Gets the package name of an <code>Object</code>.</p>
     *
     * @param object  the class to get the package name for, may be null
     * @param valueIfNull  the value to return if null
     * @return the package name of the object, or the null value
     */
    public static String getPackageName(Object object, String valueIfNull) {
        if (object == null) {
            return valueIfNull;
        }
        return getPackageName(object.getClass().getName());
    }

    /**
     * <p>Gets the package name of a <code>Class</code>.</p>
     *
     * @param cls  the class to get the package name for, may be <code>null</code>.
     * @return the package name or an empty string
     */
    public static String getPackageName(Class cls) {
        if (cls == null) {
            return StringUtil.STRING_EMPTY;
        }
        return getPackageName(cls.getName());
    }

    /**
     * <p>Gets the package name from a <code>String</code>.</p>
     *
     * <p>The string passed in is assumed to be a class name - it is not checked.</p>
     * <p>If the class is unpackaged, return an empty string.</p>
     *
     * @param className  the className to get the package name for, may be <code>null</code>
     * @return the package name or an empty string
     */
    public static String getPackageName(String className) {
        if (className == null) {
            return StringUtil.STRING_EMPTY;
        }
        int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
        if (i == -1) {
            return StringUtil.STRING_EMPTY;
        }
        return className.substring(0, i);
    }    
    
    /**
     * Get Property of 
     * @param bean
     * @param name
     * @return
     */
    public static Object getProperty(Object bean, String name) {
    	PropertyDescriptor propertyDescriptor = getPropertyDescriptor(bean.getClass(), name);
    	Method method = propertyDescriptor.getReadMethod();
    	try {
    		return method.invoke(bean, null);
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException("Property " + name + " IllegalAccessException");
		} catch (InvocationTargetException e) {
			throw new IllegalArgumentException("Property " + name + " InvocationTargetException");
		}
    }
    
    /**
     * Set Property to 
     * @param bean
     * @param name
     * @param value
     */
    public static void setProperty(Object bean, String name, Object value){
    	PropertyDescriptor propertyDescriptor = getPropertyDescriptor(bean.getClass(), name);
    	if (propertyDescriptor == null) {
			/***/
		}
    	Method method = propertyDescriptor.getWriteMethod(); 
    	try {
    		Object[] values = {value};
    		method.invoke(bean, values);
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException("Property " + name + " IllegalAccessException");
		} catch (InvocationTargetException e) {
			throw new IllegalArgumentException("Property " + name + " InvocationTargetException");
		}    	
    }
    
    /**
     * Get PropertyDescriptor of Class
     * @param clazz
     * @param name
     * @return
     */
    private static PropertyDescriptor getPropertyDescriptor(Class clazz, String name){
		PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(clazz);		
    	for (int i = 0; i < propertyDescriptors.length; i++) {
			if (StringUtil.equals(name, propertyDescriptors[i].getName())) {
				return propertyDescriptors[i];
			}			
		}
    	return null;
    }
    
    /**
     * Get Array of PropertyDescriptor of Class
     * @param clazz
     * @return
     */
    private static PropertyDescriptor[] getPropertyDescriptors(Class clazz){
    	BeanInfo beanInfo = null;
    	try {
    		beanInfo = Introspector.getBeanInfo(clazz);
		} catch (IntrospectionException e) {
			throw new IllegalArgumentException("Class " + clazz + " IntrospectionException");
		}
		return beanInfo.getPropertyDescriptors();	
    }
    
    /**
     * Copy Properties of bean Origen to bean Destination
     * @param orig
     * @param dest
     */
    public static void copyProperties(Object orig, Object dest){
    	if (orig!=null && dest!=null) {
        	PropertyDescriptor[] propertyDescriptorsOrig = getPropertyDescriptors(orig.getClass());
        	PropertyDescriptor[] propertyDescriptorsDest = getPropertyDescriptors(dest.getClass());
        	for (int i = 0; i < propertyDescriptorsOrig.length; i++) {
    			PropertyDescriptor propertyDescriptor = propertyDescriptorsOrig[i];
    			String name = propertyDescriptor.getName();
    			if (!StringUtil.equals(name, "class")) {    				
    				try {
    					Object value = getProperty(propertyDescriptor, orig, name);
    					setProperty(getPropertyDescriptor(propertyDescriptorsDest, name), dest, name, value);
    				} catch (Exception e) {
    					//System.err.println("error:"+e);
    				}
    			}
    		}
		}
    }
    
    public static void copyPropertiesNullToBlank(Object orig, Object dest){
    	if (orig!=null && dest!=null) {
        	PropertyDescriptor[] propertyDescriptorsOrig = getPropertyDescriptors(orig.getClass());
        	PropertyDescriptor[] propertyDescriptorsDest = getPropertyDescriptors(dest.getClass());
        	for (int i = 0; i < propertyDescriptorsOrig.length; i++) {
    			PropertyDescriptor propertyDescriptor = propertyDescriptorsOrig[i];
    			String name = propertyDescriptor.getName();
    			//System.out.println(propertyDescriptor.getPropertyType()+" "+ propertyDescriptor.getPropertyType().getName());
    			if (!StringUtil.equals(name, "class")) {    				
    				try {
    					Object value = getProperty(propertyDescriptor, orig, name);
    					if (value == null && propertyDescriptor.getPropertyType().getName().equals("java.lang.String")) {
    						value = StringUtil.STRING_EMPTY;
						}
    					if (value == null && value instanceof String) {
    						value = StringUtil.STRING_EMPTY;
						}
    					setProperty(getPropertyDescriptor(propertyDescriptorsDest, name), dest, name, value);
    				} catch (Exception e) {
    					//System.err.println("error:"+e);
    				}
    			}
    		}
		}
    }
    
    public static Object copyPropertiesNullToBlank(Object orig){
    	try {
        	Object dest = orig.getClass().newInstance();
        	copyPropertiesNullToBlank(orig, dest);
        	return dest;
		} catch (IllegalAccessException e) {
			// TODO: handle exception
		} catch (InstantiationException e) {
			// TODO: handle exception
		}
		return null;
    }
    
    private static Object invlokeMethod(Object bean, Method method, Object[] values){
    	try {
    		return method.invoke(bean, values);
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException("Property " + method.getName() + " IllegalAccessException");
		} catch (InvocationTargetException e) {
			throw new IllegalArgumentException("Property " + method.getName() + " InvocationTargetException");
		} 
    }
    
    public static Object getProperty(PropertyDescriptor propertyDescriptor, Object bean, String name){    	
    	Method method = propertyDescriptor.getReadMethod();
    	return invlokeMethod(bean, method, null);
    }
    
    public static void setProperty(PropertyDescriptor propertyDescriptor, Object bean, String name, Object value){
    	if (propertyDescriptor != null) {
        	Object[] values = {value};    	
        	Method method = propertyDescriptor.getWriteMethod();
        	invlokeMethod(bean, method, values); 
		} 
    }
    
    private static PropertyDescriptor getPropertyDescriptor(PropertyDescriptor[] propertyDescriptors, String name){		
    	for (int i = 0; i < propertyDescriptors.length; i++) {
			if (StringUtil.equals(name, propertyDescriptors[i].getName())) {
				return propertyDescriptors[i];
			}			
		}
    	return null;
    }

}
